// AFCDecoder.h

#pragma once

using namespace System;

namespace AFCDecoder {

const unsigned AdpcmTable[89] = 
{
  0x0007,0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x0010,0x0011,0x0013,0x0015,
  0x0017,0x0019,0x001C,0x001F,0x0022,0x0025,0x0029,0x002D,0x0032,0x0037,0x003C,0x0042,
  0x0049,0x0050,0x0058,0x0061,0x006B,0x0076,0x0082,0x008F,0x009D,0x00AD,0x00BE,0x00D1,
  0x00E6,0x00FD,0x0117,0x0133,0x0151,0x0173,0x0198,0x01C1,0x01EE,0x0220,0x0256,0x0292,
  0x02D4,0x031C,0x036C,0x03C3,0x0424,0x048E,0x0502,0x0583,0x0610,0x06AB,0x0756,0x0812,
  0x08E0,0x09C3,0x0ABD,0x0BD0,0x0CFF,0x0E4C,0x0FBA,0x114C,0x1307,0x14EE,0x1706,0x1954,
  0x1BDC,0x1EA5,0x21B6,0x2515,0x28CA,0x2CDF,0x315B,0x364B,0x3BB9,0x41B2,0x4844,0x4F7E,
  0x5771,0x602F,0x69CE,0x7462,0x7FFF
};

const int IMA_IndexTable[9] = 
{
    -1, -1, -1, -1, 2, 4, 6, 8
};
	const short afccoef[16][2] =
{{0,0},
{0x0800,0},
{0,0x0800},
{0x0400,0x0400},
{0x1000,0xf800},
{0x0e00,0xfa00},
{0x0c00,0xfc00},
{0x1200,0xf600},
{0x1068,0xf738},
{0x12c0,0xf704},
{0x1400,0xf400},
{0x0800,0xf800},
{0x0400,0xfc00},
{0xfc00,0x0400},
{0xfc00,0},
{0xf800,0}};
	public ref class Class1
	{
		// TODO: Add your methods for this class here.
		/* AFC decoder */

		public: void AFCdecodebuffer
(
 unsigned char *input, /* location of encoded source samples */
 signed short *out,   /* location of destination buffer (16 bits / sample) */
 short * histp,
 short * hist2p
)
{
	int sample;
	short nibbles[16];
	int i,j;
	char *src,*dst;
	short idx;
	short delta;
	short hist=*histp;
	short hist2=*hist2p;

	dst = (char*)out;

	src=(char*)input;
	delta = 1<<(((*src)>>4)&0xf);
	idx = (*src)&0xf;

	src++;
    
	for(i = 0; i < 16; i = i + 2) {
		j = ( *src & 255) >> 4;
		nibbles[i] = j;
		j = *src & 255 & 15;
		nibbles[i+1] = j;
		src++;
	}

	for(i = 0; i < 16; i = i + 1) {
		if(nibbles[i] >= 8) 
			nibbles[i] = nibbles[i] - 16;
	}
     
	for(i = 0; i<16 ; i = i + 1) {

		sample = (delta * nibbles[i])<<11;
		sample += ((long)hist * afccoef[idx][0]) + ((long)hist2 * afccoef[idx][1]);
		sample = sample >> 11;

		if(sample > 32767) {
			sample = 32767;
		}
		if(sample < -32768) {
			sample = -32768;
		}

		*(short*)dst = (short)sample;
		dst = dst + 2;

		hist2 = hist;
		hist = (short)sample;
        
	}

	*histp=hist;
	*hist2p=hist2;

	return;
}
				// From nocash's site: The NDS data consist of a 32bit header, followed by 4bit values (so each byte contains two
// values, the first value in the lower 4bits, the second in upper 4 bits). The 32bit header contains initial values:
//
//  Bit0-15   Initial PCM16 Value (Pcm16bit = -7FFFh..+7FFF) (not -8000h)
//  Bit16-22  Initial Table Index Value (Index = 0..88)
//  Bit23-31  Not used (zero)


// As far as I can tell, the NDS IMA-ADPCM format has one difference from standard IMA-ADPCM:
// it clamps min (and max?) sample values differently (see below).  I really don't know how much of a difference
// it makes, but this implementation is, to my knowledge, the proper way of doing things for NDS.
void ConvertImaAdpcm(unsigned char *buf, int length, unsigned char* outbuffer)
{
	unsigned long destOff = 0;
	unsigned int sampHeader = ((unsigned int*)buf)[0];// GetWord(dataOff-4);
	int decompSample = sampHeader & 0xFFFF;
	int stepIndex = (sampHeader >> 16) & 0x7F;
	//int decompSample = GetShort(dataOff);
	//int stepIndex = GetShort(dataOff+2);
	unsigned long curOffset = 4;
	((short*)outbuffer)[destOff++] = (short)decompSample;
	
	
	unsigned char compByte;
	while (curOffset < length)
	{
		compByte = buf[curOffset++];
		process_nibble(compByte, stepIndex, decompSample);
		((short*)outbuffer)[destOff++] = (short)decompSample;
		process_nibble((compByte & 0xF0) >> 4, stepIndex, decompSample);
		((short*)outbuffer)[destOff++] = (short)decompSample;
	}
}

// I'm copying nocash's IMA-ADPCM conversion method verbatim.  Big thanks to him. 
// Info is at http://nocash.emubase.de/gbatek.htm#dssound and the algorithm is described as follows:
//
//The NDS data consist of a 32bit header, followed by 4bit values (so each byte contains two values,
//the first value in the lower 4bits, the second in upper 4 bits). The 32bit header contains initial values:
//
//  Bit0-15   Initial PCM16 Value (Pcm16bit = -7FFFh..+7FFF) (not -8000h)
//  Bit16-22  Initial Table Index Value (Index = 0..88)
//  Bit23-31  Not used (zero)
//
//In theory, the 4bit values are decoded into PCM16 values, as such:
//
//  Diff = ((Data4bit AND 7)*2+1)*AdpcmTable[Index]/8      ;see rounding-error
//  IF (Data4bit AND 8)=0 THEN Pcm16bit = Max(Pcm16bit+Diff,+7FFFh)
//  IF (Data4bit AND 8)=8 THEN Pcm16bit = Min(Pcm16bit-Diff,-7FFFh)
//  Index = MinMax (Index+IndexTable[Data4bit AND 7],0,88)
//
//In practice, the first line works like so (with rounding-error):
//
//  Diff = AdpcmTable[Index]/8
//  IF (data4bit AND 1) THEN Diff = Diff + AdpcmTable[Index]/4
//  IF (data4bit AND 2) THEN Diff = Diff + AdpcmTable[Index]/2
//  IF (data4bit AND 4) THEN Diff = Diff + AdpcmTable[Index]/1
//
//And, a note on the second/third lines (with clipping-error):
//
//  Max(+7FFFh) leaves -8000h unclipped (can happen if initial PCM16 was -8000h)
//  Min(-7FFFh) clips -8000h to -7FFFh (possibly unlike windows .WAV files?)

#define IMAMax(samp) (samp > 0x7FFF) ? ((short)0x7FFF) : samp
#define IMAMin(samp) (samp < -0x7FFF) ? ((short)-0x7FFF) : samp
#define IMAIndexMinMax(index,min,max) (index > max) ? max : ((index < min) ? min : index)

void process_nibble(unsigned char data4bit, int& Index, int& Pcm16bit)
{
	//int Diff = ((Data4bit & 7)*2+1)*AdpcmTable[Index]/8;
	int Diff = AdpcmTable[Index]/8;
	if (data4bit & 1) Diff = Diff + AdpcmTable[Index]/4;
	if (data4bit & 2) Diff = Diff + AdpcmTable[Index]/2;
	if (data4bit & 4) Diff = Diff + AdpcmTable[Index]/1;

	if ((data4bit & 8)==0) Pcm16bit = IMAMax(Pcm16bit+Diff);
	if ((data4bit & 8)==8) Pcm16bit = IMAMin(Pcm16bit-Diff);
	Index = IMAIndexMinMax (Index+IMA_IndexTable[data4bit & 7],0,88);
}



void clamp_step_index(int& stepIndex)
{
    if (stepIndex < 0 ) stepIndex = 0;
    if (stepIndex > 88) stepIndex = 88;
}

void clamp_sample(int& decompSample)
{
    if (decompSample < -32768) decompSample = -32768;
    if (decompSample >  32767) decompSample =  32767;
}

	
	};
}
